module aru_reduce_cfg_pipe (
    input logic                  clk,
    input logic                  rst_n,
          aru_reduce_cfg_if.in   u_aru_cfg_if,
          aru_reduce_ctrl_if.out u_aru_reduce_transpose_if,
          aru_reduce_ctrl_if.out u_aru_reduce_stage1_if,
          aru_reduce_ctrl_if.out u_aru_reduce_stage2_if,
          aru_reduce_ctrl_if.out u_aru_reduce_stage3_if,
          aru_reduce_ctrl_if.out u_aru_reduce_div_if
);

    // Configuration pipeline depth
    localparam CFG_PIPE_DEPTH = 2;
    localparam TRANSPOSE_ID = 0;
    localparam STAGE1_ID = 1;
    localparam STAGE2_ID = 2;
    localparam STAGE3_ID = 3;
    localparam DIV_ID = 4;
    localparam STAGE_NUM = 5;

    // Configuration index counter (auto-wraparound with 1-bit width)
    logic [$clog2(CFG_PIPE_DEPTH)-1:0] cfg_idx_to_recv;
    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            cfg_idx_to_recv <= 'd0;
        end else if (u_aru_cfg_if.vld && u_aru_cfg_if.rdy) begin
            cfg_idx_to_recv <= cfg_idx_to_recv + 'd1;  // Auto-wraparound for 1-bit
        end
    end

    // Configuration storage
    typedef struct packed {
        idx_t slice_m;
        idx_t slice_n;
        logic reduce_m;
        logic reduce_n;
        logic [1:0] reduce_op;
        idx_t instr_idx;
    } cfg_t;

    cfg_t                              cfg_slt         [CFG_PIPE_DEPTH];
    logic                              cfg_vld         [CFG_PIPE_DEPTH];
    logic [             STAGE_NUM-1:0] ctrl_sent       [CFG_PIPE_DEPTH];
    logic [             STAGE_NUM-1:0] ctrl_rdy;
    logic [$clog2(CFG_PIPE_DEPTH)-1:0] ctrl_idx_to_send[     STAGE_NUM];

    // Configuration receive logic
    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            for (integer i = 0; i < CFG_PIPE_DEPTH; i = i + 1) begin
                cfg_vld[i] <= 1'b0;
                cfg_slt[i] <= 'd0;
            end
        end else begin
            for (integer i = 0; i < CFG_PIPE_DEPTH; i = i + 1) begin
                // Receive new configuration
                if (u_aru_cfg_if.vld && u_aru_cfg_if.rdy && (i == cfg_idx_to_recv)) begin
                    cfg_slt[i].slice_m   <= u_aru_cfg_if.slice_m;
                    cfg_slt[i].slice_n   <= u_aru_cfg_if.slice_n;
                    cfg_slt[i].reduce_m  <= u_aru_cfg_if.reduce_m;
                    cfg_slt[i].reduce_n  <= u_aru_cfg_if.reduce_n;
                    cfg_slt[i].reduce_op <= u_aru_cfg_if.reduce_op;
                    cfg_slt[i].instr_idx <= u_aru_cfg_if.instr_idx;
                    cfg_vld[i]           <= 1'b1;
                    // Clear configuration when all stages have been sent
                end else if (cfg_vld[i] && (ctrl_sent[i] == {STAGE_NUM{1'b1}})) begin
                    cfg_slt[i] <= 'd0;
                    cfg_vld[i] <= 1'b0;
                end
            end
        end
    end

    // Configuration send control logic
    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            for (integer i = 0; i < STAGE_NUM; i = i + 1) begin
                ctrl_idx_to_send[i] <= 'd0;
            end
            for (integer j = 0; j < CFG_PIPE_DEPTH; j = j + 1) begin
                ctrl_sent[j] <= 'd0;
            end
        end else begin
            // Update ctrl_sent and ctrl_idx_to_send for each stage
            for (integer i = 0; i < STAGE_NUM; i = i + 1) begin
                if (cfg_vld[ctrl_idx_to_send[i]] && !ctrl_sent[ctrl_idx_to_send[i]][i] && ctrl_rdy[i]) begin
                    ctrl_sent[ctrl_idx_to_send[i]][i] <= 1'b1;
                    ctrl_idx_to_send[i]               <= ctrl_idx_to_send[i] + 1'b1;  // Auto-wraparound
                end
            end

            // Clear ctrl_sent when all stages have been sent
            for (integer j = 0; j < CFG_PIPE_DEPTH; j = j + 1) begin
                if (ctrl_sent[j] == {STAGE_NUM{1'b1}}) begin
                    ctrl_sent[j] <= 'd0;
                end
            end
        end
    end

    // Ready signal generation
    assign u_aru_cfg_if.rdy = ~cfg_vld[cfg_idx_to_recv];

    // Transpose interface
    assign ctrl_rdy[TRANSPOSE_ID] = u_aru_reduce_transpose_if.rdy;
    assign u_aru_reduce_transpose_if.vld       = cfg_vld[ctrl_idx_to_send[TRANSPOSE_ID]] && 
                                                  !ctrl_sent[ctrl_idx_to_send[TRANSPOSE_ID]][TRANSPOSE_ID];
    assign u_aru_reduce_transpose_if.reduce_m = cfg_slt[ctrl_idx_to_send[TRANSPOSE_ID]].reduce_m;
    assign u_aru_reduce_transpose_if.reduce_n = cfg_slt[ctrl_idx_to_send[TRANSPOSE_ID]].reduce_n;
    assign u_aru_reduce_transpose_if.reduce_op = cfg_slt[ctrl_idx_to_send[TRANSPOSE_ID]].reduce_op;
    assign u_aru_reduce_transpose_if.slice_m = cfg_slt[ctrl_idx_to_send[TRANSPOSE_ID]].slice_m;
    assign u_aru_reduce_transpose_if.slice_n = cfg_slt[ctrl_idx_to_send[TRANSPOSE_ID]].slice_n;
    assign u_aru_reduce_transpose_if.instr_idx = cfg_slt[ctrl_idx_to_send[TRANSPOSE_ID]].instr_idx;

    // Stage1 interface
    assign ctrl_rdy[STAGE1_ID] = u_aru_reduce_stage1_if.rdy;
    assign u_aru_reduce_stage1_if.vld          = cfg_vld[ctrl_idx_to_send[STAGE1_ID]] && 
                                                  !ctrl_sent[ctrl_idx_to_send[STAGE1_ID]][STAGE1_ID];
    assign u_aru_reduce_stage1_if.reduce_m = cfg_slt[ctrl_idx_to_send[STAGE1_ID]].reduce_m;
    assign u_aru_reduce_stage1_if.reduce_n = cfg_slt[ctrl_idx_to_send[STAGE1_ID]].reduce_n;
    assign u_aru_reduce_stage1_if.reduce_op = cfg_slt[ctrl_idx_to_send[STAGE1_ID]].reduce_op;
    assign u_aru_reduce_stage1_if.slice_m = cfg_slt[ctrl_idx_to_send[STAGE1_ID]].slice_m;
    assign u_aru_reduce_stage1_if.slice_n = cfg_slt[ctrl_idx_to_send[STAGE1_ID]].slice_n;
    assign u_aru_reduce_stage1_if.instr_idx = cfg_slt[ctrl_idx_to_send[STAGE1_ID]].instr_idx;

    // Stage2 interface
    assign ctrl_rdy[STAGE2_ID] = u_aru_reduce_stage2_if.rdy;
    assign u_aru_reduce_stage2_if.vld          = cfg_vld[ctrl_idx_to_send[STAGE2_ID]] && 
                                                  !ctrl_sent[ctrl_idx_to_send[STAGE2_ID]][STAGE2_ID];
    assign u_aru_reduce_stage2_if.reduce_m = cfg_slt[ctrl_idx_to_send[STAGE2_ID]].reduce_m;
    assign u_aru_reduce_stage2_if.reduce_n = cfg_slt[ctrl_idx_to_send[STAGE2_ID]].reduce_n;
    assign u_aru_reduce_stage2_if.reduce_op = cfg_slt[ctrl_idx_to_send[STAGE2_ID]].reduce_op;
    assign u_aru_reduce_stage2_if.slice_m = cfg_slt[ctrl_idx_to_send[STAGE2_ID]].slice_m;
    assign u_aru_reduce_stage2_if.slice_n = cfg_slt[ctrl_idx_to_send[STAGE2_ID]].slice_n;
    assign u_aru_reduce_stage2_if.instr_idx = cfg_slt[ctrl_idx_to_send[STAGE2_ID]].instr_idx;

    // Stage3 interface
    assign ctrl_rdy[STAGE3_ID] = u_aru_reduce_stage3_if.rdy;
    assign u_aru_reduce_stage3_if.vld          = cfg_vld[ctrl_idx_to_send[STAGE3_ID]] && 
                                                  !ctrl_sent[ctrl_idx_to_send[STAGE3_ID]][STAGE3_ID];
    assign u_aru_reduce_stage3_if.reduce_m = cfg_slt[ctrl_idx_to_send[STAGE3_ID]].reduce_m;
    assign u_aru_reduce_stage3_if.reduce_n = cfg_slt[ctrl_idx_to_send[STAGE3_ID]].reduce_n;
    assign u_aru_reduce_stage3_if.reduce_op = cfg_slt[ctrl_idx_to_send[STAGE3_ID]].reduce_op;
    assign u_aru_reduce_stage3_if.slice_m = cfg_slt[ctrl_idx_to_send[STAGE3_ID]].slice_m;
    assign u_aru_reduce_stage3_if.slice_n = cfg_slt[ctrl_idx_to_send[STAGE3_ID]].slice_n;
    assign u_aru_reduce_stage3_if.instr_idx = cfg_slt[ctrl_idx_to_send[STAGE3_ID]].instr_idx;

    // Div interface
    assign ctrl_rdy[DIV_ID] = u_aru_reduce_div_if.rdy;
    assign u_aru_reduce_div_if.vld = cfg_vld[ctrl_idx_to_send[DIV_ID]] && !ctrl_sent[ctrl_idx_to_send[DIV_ID]][DIV_ID];
    assign u_aru_reduce_div_if.reduce_m = cfg_slt[ctrl_idx_to_send[DIV_ID]].reduce_m;
    assign u_aru_reduce_div_if.reduce_n = cfg_slt[ctrl_idx_to_send[DIV_ID]].reduce_n;
    assign u_aru_reduce_div_if.reduce_op = cfg_slt[ctrl_idx_to_send[DIV_ID]].reduce_op;
    assign u_aru_reduce_div_if.slice_m = cfg_slt[ctrl_idx_to_send[DIV_ID]].slice_m;
    assign u_aru_reduce_div_if.slice_n = cfg_slt[ctrl_idx_to_send[DIV_ID]].slice_n;
    assign u_aru_reduce_div_if.instr_idx = cfg_slt[ctrl_idx_to_send[DIV_ID]].instr_idx;

endmodule
